import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm


# 解决中文显示问题 - 尝试多种常见字体
def setup_chinese_font():
    # 尝试系统中可能存在的中文字体
    possible_fonts = [
        "SimHei", "WenQuanYi Micro Hei", "Heiti TC",
        "Microsoft YaHei", "SimSun", "KaiTi",
        "Arial Unicode MS", "NSimSun"
    ]

    # 查找可用字体
    available_fonts = []
    for font in possible_fonts:
        try:
            fm.findfont(font)
            available_fonts.append(font)
        except:
            continue

    if available_fonts:
        # 使用找到的第一个可用字体
        plt.rcParams["font.family"] = available_fonts[0]
        print(f"使用字体: {available_fonts[0]}")
        return True
    else:
        # 如果没有找到中文字体，使用默认字体并替换中文为英文
        print("警告: 未找到中文字体，将使用英文显示")
        return False


class PCA:
    def __init__(self, n_components):
        """初始化PCA模型"""
        self.n_components = n_components
        self.components_ = None  # 主成分（特征向量）
        self.explained_variance_ratio_ = None  # 方差贡献率

    def fit(self, X):
        """训练PCA模型"""
        # 1. 数据标准化：均值为0，标准差为1
        self.mean_ = np.mean(X, axis=0)
        self.std_ = np.std(X, axis=0)
        # 处理标准差为0的情况（避免除零错误）
        self.std_[self.std_ == 0] = 1
        X_scaled = (X - self.mean_) / self.std_

        # 2. 计算协方差矩阵
        cov_matrix = np.cov(X_scaled.T)

        # 3. 对协方差矩阵进行特征值分解
        eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

        # 4. 按特征值从大到小排序特征向量
        sorted_indices = np.argsort(eigenvalues)[::-1]
        sorted_eigenvalues = eigenvalues[sorted_indices]
        sorted_eigenvectors = eigenvectors[:, sorted_indices]

        # 5. 选择前n_components个主成分
        self.components_ = sorted_eigenvectors[:, :self.n_components]

        # 计算方差贡献率
        total_variance = np.sum(sorted_eigenvalues)
        self.explained_variance_ratio_ = sorted_eigenvalues[:self.n_components] / total_variance

        return self

    def transform(self, X):
        """将数据投影到主成分上"""
        X_scaled = (X - self.mean_) / self.std_
        return np.dot(X_scaled, self.components_)

    def fit_transform(self, X):
        """训练模型并将数据投影到主成分上"""
        self.fit(X)
        return self.transform(X)

    def inverse_transform(self, X_transformed):
        """
        将降维后的数据映射回原始特征空间

        参数:
        X_transformed: 降维后的数组，形状为 (n_samples, n_components)

        返回:
        X_original: 映射回原始空间的数组，形状为 (n_samples, n_features)
        """
        # 通过主成分将低维数据映射回原始空间
        X_reconstructed = np.dot(X_transformed, self.components_.T)
        # 加上均值（逆去中心化）
        X_reconstructed += self.mean_
        return X_reconstructed


